<?php
/**
 * Created by ephp
 * User: 22071
 * Date: 2019/6/14
 * Email: <zhendongdong@foxmail.com>
 */

namespace paper\support;


use ArrayAccess;
use paper\Db;
use paper\Paginate;
use paper\pdo\Query;
use paper\Relation;


/**
 * @property Paginate $paginate
 */
class Model implements ArrayAccess
{
    /**
     * @var string|null
     */
    protected $table = null;
    /**
     * @var string
     */
    protected $pk = 'id';
    /**
     * @var array
     */
    protected $data = [];

    protected $cache_data = [];

    /**
     * @var bool
     */
    protected $is_exists = false;

    protected $msg = null;


    /**ss
     * Model constructor.
     * @param array $data
     * @param array $cache_data
     */
    public function __construct($data = [], $cache_data = [])
    {
        if ($data) {
            $this->is_exists  = true;
            $this->data       = $data;
            $this->cache_data = $cache_data;
        }
    }

    /**
     * @return array
     */
    protected function rule()
    {
        return [];
    }


    public function load($data = [])
    {
        if (!$data && !is_array($data)) {
            $this->setMsg("没有任何数据被提交");
            return false;
        }
        $this->data = $data;
        return true;
    }

    public function validate($data = [])
    {
        $data     = $data ?: $this->data;
        $validate = new Validate($this->rule());
        if (!$validate->check($data)) {
            $this->setMsg($validate->getErrMsg());
            return false;
        }
        return true;
    }

    /**
     * 获取表名字
     * @return string|null
     */
    public function tabName()
    {
        if ($this->table === null) {
            $modelName   = str_replace('\\', '/', static::class);
            $modelName   = str_replace('Model', '', $modelName);
            $modelName   = toUnderScore(basename($modelName));
            $this->table = $modelName;
        }
        return $this->table;
    }

    public function getData($field)
    {
        return $this->data[$field];
    }

    public function setMsg($msg)
    {
        $this->msg = $msg;
    }

    /**
     * @return string|mixed
     */
    public function getMsg()
    {
        return $this->msg;
    }


    /**
     * 设置模型对应关系 一对一
     * @param string $model
     * @param null $pk
     * @param null $foreignKey
     * @return bool|Relation
     */
    public function belongTo($model, $pk = null, $foreignKey = null)
    {
        $relation = Relation::create(Relation::BELONG_TO, $this, $model, $pk, $foreignKey);
        return $relation;
    }

    /**
     * 设置模型对应关系 一对一
     * @param string $model
     * @param null $pk
     * @param null $foreignKey
     * @return bool|Relation
     */
    public function hasOne($model, $pk = null, $foreignKey = null)
    {
        $relation = Relation::create(Relation::HAS_ONE, $this, $model, $pk, $foreignKey);
        return $relation;
    }

    /**
     * 设置模型对应关系 一对多
     * @param string $model
     * @param null $pk
     * @param null $foreignKey
     * @return bool|Relation|Query
     */
    public function hasMany($model, $pk = null, $foreignKey = null)
    {
        $relation = Relation::create(Relation::HAS_MANY, $this, $model, $pk, $foreignKey);
        return $relation;
    }


    public function newInstance($data, $cache_data = [])
    {
        return new static($data, $cache_data);
    }


    public function getColumn($field)
    {
        return array_column($this->data, $field);
    }


    /**
     * @return string
     */
    public function getPk(): string
    {
        return $this->pk;
    }


    /**
     * @param $pk
     * @return array|bool|static
     */
    public function get($pk)
    {
        $where = [];
        if (!is_array($pk)) {
            $where[$this->pk] = $pk;
        } else {
            $where = $pk;
        }
        return self::query()->where($where)->find();
    }


    public static function model()
    {
        return new static();
    }

    /**
     * 创建数据库查询
     * 每次执行都会重新实例化
     * @return Query
     */
    public static function query()
    {
        //创建查询类并且挂载模型
        return Db::model(new static());
    }

    /**
     * @param array $data
     */
    public function setData(array $data): void
    {
        $this->is_exists = true;
        $this->data      = $data;
    }

    /**
     * 删除本数据 （必须已经查询出数据才可使用）
     * @return array|bool|mixed
     */
    public function destroy()
    {
        if ($this->is_exists) {
            return self::query()->where([$this->pk => $this->data[$this->pk]])->delete();
        }
        return false;
    }

    /**
     * 保存数据 （已经查询出数据则为更新当前模型，否则为新增）
     * @param $data
     * @return array|bool|mixed
     */
    public function save(array $data = [])
    {
        // 合并数据
        $this->data = array_merge($this->data, $data);
        // 判断是否已经查询过数据 如果是则更新当前数据 否则为新增数据
        if ($this->is_exists) {
            return self::query()->where($this->pk, '=', $this->data[$this->pk])->update($this->data);
        }
        return self::query()->insert($this->data);
    }

    /**
     * 批量插入数据 （只支持插入 不支持更新模型）
     * @param $data
     * @return array|bool|mixed
     * @throws \Exception
     */
    public function saveAll(array $data)
    {
        // 合并数据
        $this->data = array_merge($this->data, $data);
        return self::query()->insertAll($this->data);
    }


    /**
     * 获取模型数据
     * @param $where
     * @return bool|static
     * @throws \Exception
     */
    public function all($where)
    {
        return self::query()->where($where)->select();
    }

    /**
     * @param $name
     * @return mixed
     * @throws Exception
     */
    public function __get($name)
    {

        //检测是否已经存在关联关系
        $field = toUnderScore($name);
        $fun   = toCamelCase($name);

        if (array_key_exists($field, $this->cache_data)) {
            //存在关联关系，直接返回
            return $this->cache_data[$field];
        }

        //检测关联模型方法
        if (is_callable([$this, $fun], false, $callable_name)) {
            /* @var Relation $result */
            $result = $this->$fun();
            //判断返回的是否是关联对象
            $this->cache_data[$field] = $result->getData();
            return $this->cache_data[$field];
        }

        //检测模型是否定义了自定义字段方法
        $attr_method = 'get' . ucfirst($fun) . 'Attr';
        if (is_callable([$this, $attr_method], false)) {
            //执行自定义字段方法
            $result = call_user_func([$this, $attr_method], $this->data[$field] ?? null);
            //判断返回的是否是关联对象
            $this->cache_data[$field] = $result;
            //直接将自定义字段的数据返回
            return $this->cache_data[$field];
        }

        return $this->data[$field];
    }

    /**
     * @param $name
     * @param $value
     */
    public function __set($name, $value)
    {
        //检测模型是否定义了自定义字段方法
        $fun         = toCamelCase($name);
        $attr_method = 'set' . ucfirst($fun);
        if (is_callable([$this, $attr_method], false)) {
            //$this->$attr_method($value);
            call_user_func([$this, $attr_method], $value);
        } else {
            $this->data[$name] = $value;
        }
    }

    /**
     * @return array
     */
    public function toArray()
    {
        return $this->data;
    }

    /**
     * @param mixed $offset
     * @return bool
     */
    public function offsetExists($offset)
    {

        return isset($this->data[$offset]);
    }


    /**
     * @param mixed $offset
     * @return mixed
     * @throws Exception
     */
    public function offsetGet($offset)
    {
        return $this->__get($offset);
    }

    /**
     * @param mixed $offset
     * @param mixed $value
     */
    public function offsetSet($offset, $value)
    {
        $this->data[$offset] = $value;
    }


    /**
     * @param mixed $offset
     */
    public function offsetUnset($offset)
    {
        unset($this->data);
    }


    /**
     * @return false|string
     */
    public function __toString()
    {
        return json_encode($this->data);
    }
}